親愛的,幫忙去超市買 1 顆蘋果回來,如果他們有雞蛋的話,買 6 顆。
Simple logic problem that engineers should know.
前面的指令都是執行到就會做對應的事,
只有 Branch 系列指令會先做檢查,
然後決定接下來是要執行下一道指令(current_pc + 4),
還是跳到其他位置再繼續執行(current_pc + imm*2)。
行為上可以把 BRANCH 指令當作範圍較小的 JUMP 指令用,
但為了不讓 Branch Prediction 的預測方法受到影響,
規格書建議不要這樣做。
和 JUMP 指令同樣要注意的是,
如果跳到不合法的位置會觸發 instruction-address-misaligned exception。
指令格式如下:
|31 25|24 20|19 15|14 12|11 7|6 0|
+---------------------------------------------------+
| imm | rs2 | rs1 | funct3 | imm | opcode |
+---------------------------------------------------+
pc = current_pc + (rs1 == rs2 ? imm*2 : 4)
|31 25|24 20|19 15|14 12|11 7|6 0|
+---------------------------------------------------+
| imm | rs2 | rs1 | 000 | imm | opcode |
+---------------------------------------------------+
pc = current_pc + (rs1 != rs2 ? imm*2 : 4)
|31 25|24 20|19 15|14 12|11 7|6 0|
+---------------------------------------------------+
| imm | rs2 | rs1 | 001 | imm | opcode |
+---------------------------------------------------+
pc = current_pc + (rs1 < rs2 ? imm*2 : 4)
|31 25|24 20|19 15|14 12|11 7|6 0|
+---------------------------------------------------+
| imm | rs2 | rs1 | 100 | imm | opcode |
+---------------------------------------------------+
pc = current_pc + (rs1 >= rs2 ? imm*2 : 4)
|31 25|24 20|19 15|14 12|11 7|6 0|
+---------------------------------------------------+
| imm | rs2 | rs1 | 101 | imm | opcode |
+---------------------------------------------------+
pc = current_pc + ((uint32_t)rs1 < (uint32_t)rs2 ? imm*2 : 4)
|31 25|24 20|19 15|14 12|11 7|6 0|
+---------------------------------------------------+
| imm | rs2 | rs1 | 110 | imm | opcode |
+---------------------------------------------------+
pc = current_pc + ((uint32_t)rs1 >= (uint32_t)rs2 ? imm*2 : 4)
|31 25|24 20|19 15|14 12|11 7|6 0|
+---------------------------------------------------+
| imm | rs2 | rs1 | 111 | imm | opcode |
+---------------------------------------------------+
github 頁面 Tag: ITDay19
吸取昨天的經驗,
這次先實作 get_imm_b
減少指令實作的複雜度。
//instructionDecoder.cpp
int32_t INSTRUCTION_DECODER::get_imm_b()
{
auto value = sc_dt::sc_int<32>();
value(12, 12) = instruction_value(31, 31);
value(11, 11) = instruction_value(7, 7);
value(10, 5) = instruction_value(30, 25);
value(4, 1) = instruction_value(11, 8);
value <<= 19;
value >>= 19;
return value;
}
指令實作的部分很單純,就跟上面寫的一樣,
只要把依照比較結果設定 new_pc 的值就好,
這邊只列出 BEQ 指令當作範例。
//executor.cpp
...
case INSTRUCTION_DECODER_INTERFACE::BRANCH_OP:
switch (instruction_decoder->get_func3()) {
case INSTRUCTION_DECODER_INTERFACE::BEQ_FN3:
BEQ_E();
break;
...
void EXECUTOR::BEQ_E()
{
auto offset = instruction_decoder->get_imm_b();
auto rs1 = instruction_decoder->get_rs1();
auto rs2 = instruction_decoder->get_rs2();
new_pc = register_file->get_pc() +
(register_file->get_value_integer(rs1) == register_file->get_value_integer(rs1) ?
offset : 4);
}
...